# --- START OF FILE main.txt (MODIFIED) ---

from netdevsdk import *
import time
from ctypes import * # ADDED: 增加ctypes库的导入，用于定义函数参数和返回类型

class DevClass:
    def __init__(self):
        self.egsSdk = self.load_sdk()  # 加载sdk库
        if self.egsSdk: # ADDED: 确保sdk加载成功后再进行后续操作
            self.setup_sdk_functions() # ADDED: 调用新函数来设置函数原型
        self.iUserID = -1  # 登录句柄

    def load_sdk(self):
        egs_sdk = None
        try:
            egs_sdk = load_library(net_sdk_dll_path)
            print(f"动态库[{net_sdk_dll_path}]加载成功") # MODIFIED: 修改提示，使其更清晰
        except Exception as e:
            print(f"动态库[{net_sdk_dll_path}]加载失败:{e}") # MODIFIED: 修改提示，使其更清晰
        return egs_sdk

    # ADDED: 新增一个函数，用于集中设置SDK函数的参数和返回类型
    def setup_sdk_functions(self):
        """
        设置SDK函数的argtypes和restype，防止ctypes默认类型转换导致的问题。
        这是解决 iUserID 从 0x627dbab9e490 变为 0xffffffffbab9e490 的关键。
        """
        # 定义登录函数的返回类型为一个64位长整型
        self.egsSdk.NETDEV_Login_V30.restype = c_longlong
        
        # 定义按时间下载函数的参数类型
        self.egsSdk.NETDEV_GetFileByTime.argtypes = [c_longlong, POINTER(NETDEV_PLAYBACKCOND_S), c_char_p, c_int]
        self.egsSdk.NETDEV_GetFileByTime.restype = c_longlong

        # 定义登出函数的参数类型
        self.egsSdk.NETDEV_Logout.argtypes = [c_longlong]

        # 定义回放控制函数的参数类型
        self.egsSdk.NETDEV_PlayBackControl.argtypes = [c_longlong, c_int, POINTER(c_int64)]


    def sdk_init(self):
        res = self.egsSdk.NETDEV_Init()
        # 通常SDK函数成功返回0，失败返回非0
        if res != 0:
            print(f"初始化sdk失败，错误码: {res}")
        else:
            print("初始化sdk成功")

    def login(self, ip, port, username, password):
        login_info = NETDEV_DEVICE_LOGIN_INFO_S()
        login_info.szIPAddr = ip
        login_info.dwPort = port
        login_info.szUserName = username
        login_info.szPassword = password
        login_info.dwLoginProto = 0
        login_info.dwDeviceType = 0

        se_log_info = NETDEV_SELOG_INFO_S()
        self.iUserID = self.egsSdk.NETDEV_Login_V30(pointer(login_info), pointer(se_log_info))

        # MODIFIED: 修正登录结果的判断逻辑
        # 失败时返回值为0或负数，成功时返回一个大于0的句柄
        if self.iUserID <= 0:
            print("登陆失败，错误码：", self.egsSdk.NETDEV_GetLastError())
            return False # MODIFIED: 返回布尔值以表示失败
        
        print(f"登录成功，用户句柄 iUserID: {hex(self.iUserID)}") # ADDED: 打印十六进制的句柄以供调试
        return True # MODIFIED: 返回布尔值以表示成功

    def download(self, channel_id, begin_time, end_time, save_filename):
        # 初始化结构体
        playback_cond = NETDEV_PLAYBACKCOND_S()

        # 填充结构体参数
        playback_cond.dwChannelID = channel_id
        playback_cond.tBeginTime = begin_time
        playback_cond.tEndTime = end_time
        playback_cond.dwLinkMode = 1
        playback_cond.dwDownloadSpeed = 8  # 下载倍速

        print(f"开始下载，用户句柄 iUserID: {hex(self.iUserID)}") # ADDED: 打印用于下载的句柄，确认其正确性
        get_file = self.egsSdk.NETDEV_GetFileByTime(self.iUserID, byref(playback_cond), save_filename.encode('utf-8'), 0)
        
        if get_file <= 0: # MODIFIED: 下载句柄通常也大于0才有效
            print("启动按时间下载失败，错误码:", self.egsSdk.NETDEV_GetLastError())
            return
        
        print(f"获取下载句柄成功: {hex(get_file)}，开始查询进度...")
        time.sleep(1)

        play_time = c_int64(0)
        
        # MODIFIED: 循环逻辑优化
        while True:
            # 获取当前已下载到的时间点
            self.egsSdk.NETDEV_PlayBackControl(get_file, 3, byref(play_time))
            
            # 检查下载是否完成
            if play_time.value >= end_time:
                print("当前下载到：100.00%")
                break # 下载完成，退出循环
            
            # 计算并打印进度
            progress = ((play_time.value - begin_time) / (end_time - begin_time)) * 100
            print(f"当前下载到：{progress:.2f}%%")
            
            # 检查是否出错或停止
            if progress < 0:
                print("下载进度异常，可能已中断。")
                break

            time.sleep(1)
        
        print("下载任务结束。")


    def logout(self):
        if self.iUserID <= 0: # ADDED: 如果未成功登录，则无需登出
            return
        res = self.egsSdk.NETDEV_Logout(self.iUserID)
        # MODIFIED: 修正登出结果的判断逻辑，SDK函数通常成功返回0
        if res != 0:
            print("登出失败，错误码：", self.egsSdk.NETDEV_GetLastError())
        else:
            print("登出成功")


    def sdk_cleanup(self):
        res = self.egsSdk.NETDEV_Cleanup()
        # 通常SDK函数成功返回0，失败返回非0
        if res != 0:
            print(f"释放sdk失败，错误码: {res}")
        else:
            print("释放sdk成功")

    def __del__(self):
        self.logout()
        self.sdk_cleanup()


if __name__ == '__main__':
    dev = DevClass()
    dev.sdk_init()

    # 输入参数
    ip = b"192.168.1.30"
    port = 80
    username = b"admin"
    password = b"Nanjing@8625"
    
    # MODIFIED: 通过判断login的返回值来决定是否执行后续操作
    if dev.login(ip, port, username, password):
        channel_id = 1
        begin_time = 1748931720
        end_time = 1748931780
        save_filename = "./video/{}.mp4".format(begin_time)
        dev.download(channel_id, begin_time, end_time, save_filename)

# --- The original log file content is omitted for brevity ---